/*
 * ModuleInstanceDescription.java
 * 
 * last update: 16.01.2010 by Stefan Saru
 * 
 * author:	Alec(panovici@elcom.pub.ro)
 * 
 * Obs:
 */
package engine;

import java.util.*;
import middle.*;

/**
 * Describes the way a module is instantiated (it describes
 * the assignements between 
 * the container module and the submodule ports).
 */
//TODO this should be derived from ScopeInstanceDescription .
class ModuleInstanceDescription extends Symbol{

	/**
	 * Contains the Expressions (these refer to the parent !)
	 * descriptions to be linked
	 * to ports in an odered list. 
	 */
	Vector portsVector;
	Hashtable portsHash;

	/**
	 * Contains a list of (name, ExpressionDescription
	 * (these refer to the parent !))
	 * pairs to be linked to ports by names. 
  Hashtable portsHash;

  /**
	 * The descriptor of the submodule.
	 */
	transient ModuleFactory descriptor; 

	/**
	 * The name of descriptor(if forward referenced). Must be resolved at 
	 * instantiation time.
	 */
	String descriptorName;

	/**
	 * Holds the expressions that parameters will be assigned to
	 */
	Vector parameters;

	/**
	 * The primitve Strength (If this is a primitive instance).
	 */
	byte strength;
	Delay3Description delays;

	/**
	 * Creates a new ModuleInstanceDescription object.
	 * @param descriptorName the name of the module (if the name should be
	 *                       resolved to a Description at instantiation time.
	 * @param descriptor the ModuleDescription (if the name coul be resolved
	 *                       at parse time (can be null, and in this case, the
	 *                       named is resolved at instantiation time)
	 * @param strength if this is a UDP (or standard) primitive instance,
	 *                       holds the strength ( the default strength should be
	 *                       explicitely given)
	 * @param portsHash holds a hashtable with (portname, to-be-assigned-to
	 *                       expression) pairs, if the instantiation gives the
	 *                       connections in this form. One of the portsHash and
	 *                       portsVector parameters should be null.
	 * @param portsVector holds the values to be assigned (in the given order) to
	 *                       the ports.
	 */

	ModuleInstanceDescription(String name, int lineNo,
			String descriptorName,
			ModuleFactory descriptor,
			byte strength,
			Delay3Description delays,
			Vector portsVector,
			Hashtable portsHash,
			Vector parameters){
		super(name, lineNo);
		if(portsVector != null && portsHash != null)
			throw new Error("ModuleInstanceDescription.<init>: portsVector " +
			"!= null && portsHash != null");
		this.portsVector = portsVector;
		this.portsHash = portsHash;
		this.descriptor = descriptor;
		this.descriptorName = descriptorName;
		this.parameters = parameters;
		this.delays = delays;
		this.strength = strength;
	}

	Vector instantiateParams (NameSpace ns)
	throws ParseException, InterpretTimeException
	{

		if((parameters == null) || parameters.isEmpty()) return null;

		Vector results = new Vector(parameters.size());
		for (Enumeration e = parameters.elements() ; e.hasMoreElements() ;) {
			results.addElement(((ExpressionDescription) e.nextElement()).instantiate(ns).evaluate());
		}
		return results;
	}

	/**
	 * Finds the right descriptor and sets
	 * it up for the next instantiation.
	 */
	void resolveDescriptor(NameSpace parent) throws ParseException {
		if (descriptor == null) //"late" linking ?
			descriptor = ModuleLoader.getModuleByName(descriptorName);
		if (descriptor == null)
			throw new ParseException(parent.desc.toString(lineNo) +
					": error:undefined module: " +
					descriptorName);
		try {
			descriptor.setParams(instantiateParams(parent),
					delays == null ? null :
						delays.instantiate(parent));
		} catch (InterpretTimeException iex) {
			throw new ParseException(iex.getMessage());
		}
	}

	/**
	 * If 'by name' connection, order the ports to match
	 * descriptor.ConnectionList order.
	 */
	void orderPorts(Vector conList, Hashtable conHash) throws ParseException
	{
		//set up the links:
		if(portsVector == null){
			//'by name' connection list: order the arguments into portVector
			portsVector = new Vector();
			portsVector.setSize(portsHash.size());
			for (Enumeration e = portsHash.elements() ; e.hasMoreElements() ;) {
				ConnectionDescription cd = (ConnectionDescription) e.nextElement();
				int index =
					conList.indexOf(conHash.get(cd.name));
				if (index == -1)
					throw new ParseException(": error : port \"" +
							cd.name + "\" not found");
				portsVector.setElementAt(cd.portExpDesc, index);
			}
		}
	}

	Object instantiate (NameSpace parent, ScopeNode scope) throws ParseException {

		try {
			resolveDescriptor(parent);
		} catch (ParseException pex) {
			throw new ParseException(parent.desc.toString(lineNo) + pex);
		}

		if (parent != null) //top module doesn't have descriptor & parent
			xConsole.debug("descriptor is: " + descriptor + "parent is: " + parent +
					"scope is: " + scope);
		Object o =
			descriptor. createNewModule(parent, this, strength,
					scope.addScope(Constants.moduleIcon,
							name));
		descriptor.unsetParams();
		return  o;
	}

	public int getType(){
		return moduleType;
	}

}














